<?php

namespace App\Library;

use App\Models\Admin\AdminUser;
use App\Models\Admin\Configs;
use App\Models\Admin\TcAuther;
use App\Models\Admin\TencentOrder;
use App\Models\Admin\TencentStatistics;
use App\Models\Admin\TencentTemp;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Qcloud\Cos\Client;
use Illuminate\Support\Str;

/**
 * Class TencentHandle
 * @package App\Library
 * 下载腾讯云订单并处理数据
 * 服务器版 是通过 swoole 服务运行的，所以本文件修改的功能代码需要重启swoole才能生效，php artisan javascript:swool
 * 云开发版是通过定时触发器运行的，不需要重启服务
 */
class TencentHandle
{
    public $config;
    public $data;
    static $object;
    public $goodsName;
    public $productInfo;
    public $payerMode;
    public $actionType;
    public $clientType;
    public $money;
    public $sales_uin;
    public $owner_uin;

    public function __construct(){
        $con = Configs::query()
            ->where("mall_id", request()->mallData["id"])
            ->select("tencent_secretid", "tencent_secrekey")
            ->first();
        if (!$con->tencent_secretid || !$con->tencent_secrekey){
            throw new \Exception("腾讯云参数未配置");
        }
        $this->config = $con;
    }

    /**
     * @return TencentHandle
     */
    public static function getInstance(){
        if(empty(self::$object)){
            self::$object = new self();
        }
        return self::$object;
    }

    public function handleOrderData($data = []){
        $this->data = $data;
        try {
            $orderData = $this->downData();
            /** @var TencentTemp $object */
            $object = $data["object"];
            $this->handleData($object->user_id, $orderData);
            $object->address = $this->handleCsv($object->name, $orderData);
            $statisticsData = $this->handleStatistics($orderData);
            foreach ($statisticsData as $f => $item){
                if(is_array($item)) {
                    $statisticsData[$f] = json_encode($item, JSON_UNESCAPED_UNICODE);
                }
            }
            $statisticsData = array_merge($statisticsData, ["mall_id" => request()->mallData["id"], "relate_id" => $object->id]);
            TencentStatistics::query()->insert($statisticsData);
            $object->generate_time = Carbon::now()->toDateTimeString();
            $object->status = 3;
            $object->save();
        }catch (\Exception $e){
            UpTools::writeLog($e->getLine()."---:".$e->getMessage().$e->getTraceAsString());
        }
    }

    public function downData(){
        if(empty($this->data["startTime"]) || empty($this->data["endTime"])) {
            $start_time = TencentOrder::query()
                ->limit(1)
                ->orderBy("creat_time", "desc")
                ->value("creat_time");
            if (!$start_time) {
                $start_time = "2019-01-01";
            }
            $start_time = strtotime($start_time);
            $end_time = time();
        }else{
            $start_time = strtotime($this->data["startTime"]);
            $end_time = strtotime($this->data["endTime"]);
        }
        $page = 0;
        $limit = 1000;
        $count = 0;
        $data = [];
        while($start_time < $end_time){
            $over = $start_time + 86400 * 10;
            $requestParams = [
                'Offset' => $page * $limit, 'Limit' => $limit, "CreatTimeRangeStart" => date("Y-m-d H:i:s", $start_time),
                "CreatTimeRangeEnd" => date("Y-m-d H:i:s", $over), "Order" => 10000
            ];
            $res = TcScfUtil::getInstance($this->config->tencent_secretid, $this->config->tencent_secrekey, 'partner')
                ->describeAgentDealsCache($requestParams);
            $page++;
            $count = $count + (!empty($res["data"]) ? count($res["data"]["AgentDealSet"]) : 0);
            if($count >= $res["data"]["TotalCount"]){
                $start_time = $over;
                $count = $page = 0;
            }
            foreach ($res['data']['AgentDealSet'] as $item){
                foreach ($item as $k => $v){
                    if($k == "GoodsPrice"){
                        $v = $v["RealTotalCost"] / 100;
                    }elseif($k == "ProductInfo"){
                        foreach ($v as &$temp){
                            foreach ($temp as $ks => $vs){
                                $temp[Str::snake($ks)] = $vs;
                                unset($temp[$ks]);
                            }
                        }
                        unset($temp);
                        $v = json_encode($v, JSON_UNESCAPED_UNICODE);
                    }elseif($k == "VoucherDecline" && $v){
                        $v = $v / 100;
                    }
                    $item[Str::snake($k)] = $v;
                    unset($item[$k]);
                }
                $item["mall_id"] = request()->mallData["id"];
                $data[] = $item;
                if(!TencentOrder::query()->where(["deal_name" => $item["deal_name"], "mall_id" => $item["mall_id"]])->count()) {
                    TencentOrder::query()->insert($item);
                }
            }
        };
        $last_names = array_column($data,'creat_time');
        array_multisort($last_names,SORT_ASC, $data);
        return $data;
    }

    public function handleData($user_id, &$orderData){
        $userData = AdminUser::query()->where("id", $user_id)->first();
        if($userData){
            if ($userData->position != "超级管理员") {
                $tt = DB::table('permissions as p')
                    ->leftJoin('admin_permission_role as pr', 'pr.permission_id', '=', 'p.id')
                    ->leftJoin('admin_role_user as ru', 'ru.role_id', '=', 'pr.role_id')
                    ->where("ru.user_id", $user_id)
                    ->where("p.name", "admin.tencent.checkAllOrder")
                    ->count();
                if (!$tt) {
                    $sdf = TcAuther::query()->where(["mall_id" => request()->mallData["id"], "admin_id" => $user_id])->select("id")->with("sale_list")->first();
                    if(!empty($sdf["sale_list"])) {
                        $saleUin = array_column($sdf["sale_list"]->toArray(), "sales_uin");
                        foreach ($orderData as $k => $item){
                            if(!in_array($item["sales_uin"], $saleUin)){
                                unset($orderData[$k]);
                            }
                        }
                        $orderData = array_values($orderData);
                    }else{
                        $orderData = [];
                    }
                }
            }
        }
    }

    public function handleCsv($name, $orderData = []){
        $headlist = [
            "订单号", "产品名称", "产品详情", "提单人",	"提单时间", "客户类型"	, "业务员", "业务员ID", "付款人",
            "付款时间", "订单状态", "数量", "单价", 	"优惠信息", "费用（元）"
        ];
        $keyName = [
            "deal_name", "goods_name", "product_info", "creater", "creat_time", "client_type", "sales_name",
            "sales_uin", "payer", "pay_end_time", "deal_status", "goods_num", "unit_price", "voucher_decline",
            "goods_price"
        ];
        $data = [];
        $agentData = [];
        foreach ($keyName as $key){
            foreach ($orderData as $i => $item){
                if($key == "product_info" && !empty($item[$key])){
                    $item[$key] = @json_decode($item[$key], true);
                    $item[$key] = array_map(function ($var){
                        return implode(":", $var);
                    }, $item[$key]);
                    $item[$key] = implode(" ", $item[$key]);
                }elseif($key == "client_type" && !empty($item[$key])){
                    $item[$key] = $item[$key] == "new" ? "新拓" : ($item[$key] == "old" ? "存量" : ($item[$key] == "assign" ? "指派" : $item[$key]));
                }elseif($key == "sales_name" && !empty($item["sales_uin"])){
                    if(!isset($agentData[$item["sales_uin"]])) {
                        $res = TcScfUtil::getInstance($this->config->tencent_secretid, $this->config->tencent_secrekey, 'partner')
                            ->describeSalesmans(["Offset" => 0, "Limit" => 1, "SalesUin" => $item["sales_uin"]]);
                        $agentData[$item["sales_uin"]] = !empty($res["data"]['AgentSalesmanSet']) ? $res["data"]['AgentSalesmanSet'][0]["SalesName"] : "";
                    }
                    $item[$key] = $agentData[$item["sales_uin"]];
                }elseif($key == "voucher_decline" && isset($item[$key])){
                    $item[$key] = $item[$key] ? "优惠{$item[$key]}元" : "";
                }elseif($key == "unit_price"){
                    if(!empty($item["goods_price"])){
                        $item[$key] = empty($item["goods_num"]) ? $item["goods_price"] : $item["goods_price"] / $item["goods_num"];
                    }else{
                        $item[$key] = 0;
                    }
                }elseif($key == "goods_price" && isset($item["goods_price"])){
                    $item[$key] = $item[$key] - $item["voucher_decline"];
                }
                $data[$i][] = isset($item[$key]) ? $item[$key] : "";
            }
        }
        global $scf_data;
        if(empty($scf_data["IS_SCF"])){
            $newPath = public_path() . '/tencentAnalysis/';
        }else{
            $newPath = '/tmp/tencentAnalysis/';
        }
        $this->ajaxExport($newPath, $name, $data, $headlist);
        if(!empty($scf_data["IS_SCF"])){ // 存到桶里
            $bucket = $scf_data['system']['bucketConfig']['bucket'];
            require  base_path().'/vendor/cos-php-sdk/autoload.php';
            $cosClient = new Client(config('app.tengxunyun'));
            $res = $cosClient->putObject(array(
                    'Bucket' => $bucket,
                    'Key' =>  "tencentAnalysis/".$name,
                    'Body' => fopen($newPath.$name, 'rb'),
                    'ServerSideEncryption' => 'AES256')
            );
            $url = $res["ObjectURL"];
        }else{
            $url = "/tencentAnalysis/{$name}";
        }
        return $url;
    }

    /**
     * 异步处理超大csv数据
     * @newPath 目录
     * @fileName 输出Excel文件名
     * @data 导出数据
     * @headlist 第一行,列名
     */
    public function ajaxExport($newPath, $fileName, $data = array(), $headlist = array())
    {
        if (!file_exists($newPath . $fileName)) {
            if (!is_dir($newPath)) {
                mkdir($newPath, 0777, true);
            }
            $fp = fopen($newPath . $fileName, 'a+');
            //输出Excel列名信息
            foreach ($headlist as $key => $value) {
                //CSV的Excel支持GBK编码，一定要转换，否则乱码
                $headlist[$key] = iconv('utf-8', 'gbk', $value);
            }

            //将数据通过fputcsv写到文件句柄
            fputcsv($fp, $headlist);
        }

        $fp = fopen($newPath . $fileName, 'a+');

        //计数器
        $num = 0;

        //每隔$limit行，刷新一下输出buffer，不要太大，也不要太小
        $limit = 100000;

        //逐行取出数据，不浪费内存
        $count = count($data);
        for ($i = 0; $i < $count; $i++) {
            $num++;

            //刷新一下输出buffer，防止由于数据过多造成问题
            if ($limit == $num) {
                ob_flush();
                flush();
                $num = 0;
            }

            $row = $this->handleRowData($data[$i]);
            fputcsv($fp, $row);
        }
        return true;
    }

    public function handleRowData($data)
    {
        $row = $data;
        foreach ($row as $key => $value) {
            $value = $this->check($value);
            $row[$key] = mb_convert_encoding($value, 'GBK', 'UTF-8');
        }
        return $row;
    }

    //判断是否含有英文逗号，英文引号
    public function check($value)
    {
        if (!$this->checkJson($value)) {
            $isInt = is_int($value) ? true : false;
            $isFloat = is_float($value) ? true : false;

            $value = rtrim($value, "\r\n");
            $value = str_replace(",", "，", $value);

            $value = is_string($value) ? $value . "\t" : $value;
            $value = $isInt ? intval($value) : $value;
            $value = $isFloat ? floatval($value) : $value;
        }
        return $value;
    }

    // 判断是否为json数据
    private function checkJson($value)
    {
        $data = json_decode($value);
        if ($data && (is_object($data)) || is_array($data)) {
            return true;
        }
        return false;
    }

    public function handleStatistics($data){
        $sales = array_unique(array_column($data, "sales_uin"));
        $salesData = [];
        foreach ($sales as $sales_uin){
            if(empty($sales_uin)){
                continue;
            }
            $res = TcScfUtil::getInstance($this->config->tencent_secretid, $this->config->tencent_secrekey, 'partner')->describeSalesmans([
                "Offset" => 0, "Limit" => 1, "SalesUin" => $sales_uin
            ]);
            $salesData[$sales_uin] = [
                "sales_uin" => $sales_uin,
                "sales_name" => !empty($res["data"]['AgentSalesmanSet']) ? $res["data"]['AgentSalesmanSet'][0]["SalesName"] : ""
            ];
        }
        $this->goodsName = array_column($data, "goods_name");
        $this->sales_uin = array_column($data, "sales_uin");
        $this->owner_uin = array_column($data, "owner_uin");
        $this->productInfo = array_column($data, "product_info");
        $this->productInfo = array_map(function ($val){
            return (array)@json_decode($val, true);
        }, $this->productInfo);
        $this->payerMode = array_column($data, "payer_mode"); // 0：自付；1：代付
        $this->actionType = array_column($data, "action_type"); // 订单操作类型，purchase（新购），renew（续费），modify（配置变更）
        $this->clientType = array_column($data, "client_type"); // 客户类型（new：新拓；old：存量；assign：指派）
        $this->money = array_map(function ($val){
            return $val["goods_price"] - $val["voucher_decline"];
        }, $data);
        $statisticsData = [
            'all' => [], "purchase" => [], "renew" => [], "modify" => [], "sales_data" => [],
            "sales_list" => array_values($salesData), "money" => 0, "achieve" => 0, "sales_money" => [],
            "owner_uin_money" => [], "product_money" => []
        ];
        foreach ($this->goodsName as $key => $name){
            $statisticsData["money"] += $this->money[$key];
            if(strpos($name, "短信") === false && strpos($name, "域名") === false) {
                $statisticsData["achieve"] += $this->money[$key];
            }
            if($this->actionType[$key] != "refund") {
                if (strpos($name, "数据库") !== false) {
                    $this->clientType($name, $key, $statisticsData["all"]);
                    if(!empty($this->sales_uin[$key])) {
                        $this->clientType($name, $key, $statisticsData["sales_data"][$this->sales_uin[$key]]["all"]);
                    }
                    if ($this->actionType[$key] == "purchase") {
                        $this->clientType($name, $key, $statisticsData["purchase"]);
                        if(!empty($this->sales_uin[$key])) {
                            $this->clientType($name, $key, $statisticsData["sales_data"][$this->sales_uin[$key]]["purchase"]);
                        }
                    } elseif ($this->actionType[$key] == "renew") {
                        $this->clientType($name, $key, $statisticsData["renew"]);
                        if(!empty($this->sales_uin[$key])) {
                            $this->clientType($name, $key, $statisticsData["sales_data"][$this->sales_uin[$key]]["renew"]);
                        }
                    } elseif ($this->actionType[$key] == "modify") {
                        $this->clientType($name, $key, $statisticsData["modify"]);
                        if(!empty($this->sales_uin[$key])) {
                            $this->clientType($name, $key, $statisticsData["sales_data"][$this->sales_uin[$key]]["modify"]);
                        }
                    }
                } elseif (strpos($name, "服务器") !== false) {
                    foreach ($this->productInfo[$key] as $item) {
                        if ($item["name"] == "机型") {
                            $this->clientType($item["value"], $key, $statisticsData["all"]);
                            if(!empty($this->sales_uin[$key])) {
                                $this->clientType($item["value"], $key, $statisticsData["sales_data"][$this->sales_uin[$key]]["all"]);
                            }
                            if ($this->actionType[$key] == "purchase") {
                                $this->clientType($item["value"], $key, $statisticsData["purchase"]);
                                if(!empty($this->sales_uin[$key])) {
                                    $this->clientType($item["value"], $key, $statisticsData["sales_data"][$this->sales_uin[$key]]["purchase"]);
                                }
                            } elseif ($this->actionType[$key] == "renew") {
                                $this->clientType($item["value"], $key, $statisticsData["renew"]);
                                if(!empty($this->sales_uin[$key])) {
                                    $this->clientType($item["value"], $key, $statisticsData["sales_data"][$this->sales_uin[$key]]["renew"]);
                                }
                            } elseif ($this->actionType[$key] == "modify") {
                                $this->clientType($item["value"], $key, $statisticsData["modify"]);
                                if(!empty($this->sales_uin[$key])) {
                                    $this->clientType($item["value"], $key, $statisticsData["sales_data"][$this->sales_uin[$key]]["modify"]);
                                }
                            }
                        }
                    }
                }
            }
            if(!empty($this->sales_uin[$key]) && isset($statisticsData["sales_data"][$this->sales_uin[$key]])) {
                if(!isset($statisticsData["sales_data"][$this->sales_uin[$key]]["money"]))
                    $statisticsData["sales_data"][$this->sales_uin[$key]]["money"] = 0;
                if(!isset($statisticsData["sales_data"][$this->sales_uin[$key]]["achieve"]))
                    $statisticsData["sales_data"][$this->sales_uin[$key]]["achieve"] = 0;
                $statisticsData["sales_data"][$this->sales_uin[$key]]["money"] += $this->money[$key];
                if(strpos($name, "短信") === false && strpos($name, "域名") === false) {
                    $statisticsData["sales_data"][$this->sales_uin[$key]]["achieve"] += $this->money[$key];
                }
            }
            // 产品统计
            if(!isset($statisticsData["product_money"][$name])) {
                $statisticsData["product_money"][$name] = 0;
            }
            $statisticsData["product_money"][$name] += $this->money[$key];
            // 业务员统计
            if(!empty($this->sales_uin[$key])) {
                if(!isset($statisticsData["sales_money"][$this->sales_uin[$key]])) {
                    $statisticsData["sales_money"][$this->sales_uin[$key]] = 0;
                }
                $statisticsData["sales_money"][$this->sales_uin[$key]] += $this->money[$key];
            }
            // 客户统计
            if(!empty($this->owner_uin[$key])) {
                if(!isset($statisticsData["owner_uin_money"][$this->owner_uin[$key]])) {
                    $statisticsData["owner_uin_money"][$this->owner_uin[$key]] = 0;
                }
                $statisticsData["owner_uin_money"][$this->owner_uin[$key]] += $this->money[$key];
            }
        }
        $statisticsData["all"] = array_values($statisticsData["all"]);
        $statisticsData["purchase"] = array_values($statisticsData["purchase"]);
        $statisticsData["renew"] = array_values($statisticsData["renew"]);
        $statisticsData["modify"] = array_values($statisticsData["modify"]);
        foreach ($statisticsData["sales_data"] as &$item){
            $item["all"] = isset($item["all"]) ? array_values($item["all"]) : [];
            $item["purchase"] = isset($item["purchase"]) ? array_values($item["purchase"]) : [];
            $item["renew"] = isset($item["renew"]) ? array_values($item["renew"]) : [];
            $item["modify"] = isset($item["modify"]) ? array_values($item["modify"]) : [];
            $item["money"] = isset($item["money"]) ? $this->price($item["money"]) : 0;
            $item["achieve"] = isset($item["achieve"]) ? $this->price($item["achieve"]) : 0;
        }
        unset($item);
        arsort($statisticsData["sales_money"]);
        arsort($statisticsData["owner_uin_money"]);
        arsort($statisticsData["product_money"]);
        $sales_money = array_slice($statisticsData["sales_money"], 0, 5, true);
        $owner_uin_money = array_slice($statisticsData["owner_uin_money"], 0, 5, true);
        $product_money = array_slice($statisticsData["product_money"], 0, 5, true);
        $statisticsData["sales_money"] = $statisticsData["owner_uin_money"] = $statisticsData["product_money"] = [];
        foreach ($sales_money as $k => $money){
            if(!isset($rate)){
                $rate = $money;
            }
            $statisticsData["sales_money"][] = [
                "name" => isset($salesData[$k]) ? $salesData[$k]["sales_name"] : $k,
                "money" => $this->price($money),
                "rate" => empty($rate) ? 0 : sprintf("%.2f", $money/$rate*100),
            ];
        }
        unset($rate);
        foreach ($owner_uin_money as $k => $money){
            if(!isset($temp[$k])) {
                $res = TcScfUtil::getInstance($this->config->tencent_secretid, $this->config->tencent_secrekey, "partner")
                    ->describeAgentAuditedClients(['ClientUin' => strval($k)]);
                $temp[$k] = !empty($res['data']['AgentClientSet']) ? $res['data']['AgentClientSet'][0]["ClientName"] : $k;
            }
            if(!isset($rate)){
                $rate = $money;
            }
            $statisticsData["owner_uin_money"][] = [
                "name" => $temp[$k] ?? $k,
                "money" => $this->price($money),
                "rate" => empty($rate) ? 0 : sprintf("%.2f", $money/$rate*100),
            ];
        }
        unset($rate);
        foreach ($product_money as $k => $money){
            if(!isset($rate)){
                $rate = $money;
            }
            $statisticsData["product_money"][] = [
                "name" => $k,
                "money" => $this->price($money),
                "rate" => empty($rate) ? 0 : sprintf("%.2f", $money/$rate*100),
            ];
        }
        unset($rate);
        $statisticsData["money"] = $this->price($statisticsData["money"]);
        $statisticsData["achieve"] = $this->price($statisticsData["achieve"]);
        return $statisticsData;
    }

    public function clientType($name, $key, &$statisticsData){
        if(!isset($statisticsData[$name])){
            $statisticsData[$name] = [
                "name" => "", "new_money" => 0, "old_money" => 0, "assign_money" => 0, "total_money" => 0,
                "new_selfpay_money" => 0, "new_otherpay_money" => 0, "old_selfpay_money" => 0,
                "old_otherpay_money" => 0, "assign_selfpay_money" => 0, "assign_otherpay_money" => 0,
            ];
            $statisticsData[$name]["name"] = $name;
        }
        switch ($this->clientType[$key]) {
            case "new":
                $this->payerMode("new", $key, $statisticsData[$name]);
                $statisticsData[$name]["new_money"] += $this->money[$key];
                break;
            case "old":
                $this->payerMode("old", $key, $statisticsData[$name]);
                $statisticsData[$name]["old_money"] += $this->money[$key];
                break;
            case "assign":
                $this->payerMode("assign", $key, $statisticsData[$name]);
                $statisticsData[$name]["assign_money"] += $this->money[$key];
                break;
            default:
                $this->money[$key] = 0;
                break;
        }
        $statisticsData[$name]["total_money"] += $this->money[$key];
        array_walk($statisticsData[$name], function (&$value){
            if(is_numeric($value)){
                $value = $this->price($value);
            }
        });
    }

    public function payerMode($name, $key, &$statisticsData){
        switch ($this->payerMode[$key]) {
            case "0": // 自付
                $statisticsData["{$name}_selfpay_money"] += $this->money[$key];
                break;
            case "1": // 代付
                $statisticsData["{$name}_otherpay_money"] += $this->money[$key];
                break;
        }
    }

    public function price($price){
        return number_format($price, 2, '.', '');
    }
}